/*
 *FastExcel,(c) copyright 2009 yAma<guooscar@gmail.com>.
 *WEB: http://fastexcel.sourceforge.net
 *
 * This library is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
 * USA.
 */
package edu.npu.fastexcel.biff.parser.cell;

import edu.npu.fastexcel.biff.Types;
import edu.npu.fastexcel.biff.parser.ParserException;
import edu.npu.fastexcel.biff.parser.globals.Format;
import edu.npu.fastexcel.biff.parser.globals.XF;
import edu.npu.fastexcel.biff.read.RecordReader;
import edu.npu.fastexcel.biff.record.ReadOnlyRecord;
import edu.npu.fastexcel.common.util.NumUtil;
import edu.npu.fastexcel.common.util.StringUtil;
import edu.npu.fastexcel.compound.io.ReadException;
import java.text.DecimalFormat;

/**
 *
 * @author <a href="hedyn@foxmail.com">HeDYn</a>
 * @version 0.1.20110820
 */
public class FormulaParser extends CellParser {

    private static DecimalFormat decimalFormat = new DecimalFormat("#.##############");

    public FormulaParser() {
        super(Types.FORMULA);
    }

    protected FormulaParser(int type) {
        super(type);
    }

    public void parse() throws ParserException {
        if (b[off + 6] == 0 && b[off + 12] == -1 && b[off + 13] == -1) {
            parseStringFormula();
        } else if (b[off + 6] == 1 && b[off + 12] == -1 && b[off + 13] == -1) {
            parseBooleanFormula();
        } else if (b[off + 6] == 2 && b[off + 12] == -1 && b[off + 13] == -1) {
            parseErrorFormula();
        } else if (b[off + 6] == 3 && b[off + 12] == -1 && b[off + 13] == -1) {
            parseEmptyStringFormula();
        } else {
            parseNumberFormula();
        }
    }

    protected void parseNumberFormula() {
        double value = NumUtil.getIEEEDouble(b, off + 6);
        String sValue = Double.toString(value);
        int i = sValue.indexOf('.') + 1;
        if (sValue.length() - i == 15) {
            value = Double.valueOf(decimalFormat.format(value)).doubleValue();
        }

        XF x = workBookGlobalsStream.getXF(xf);
        if (x != null) {
            Format f = workBookGlobalsStream.getFormat(x.getFormat());
            if (f != null) {
                String str = f.format(new Double(value), workBookGlobalsStream,
                        context.getSetting());
                sheetStream.setContent(r, c, str);
                return;
            }
        }
        sheetStream.setContent(r, c, value + "");
    }

    protected void parseBooleanFormula() {
        boolean value = b[off + 8] == 1;
        sheetStream.setContent(r, c, value + "");
    }

    protected void parseErrorFormula() {
        int errorCode = b[off + 4];
        sheetStream.setContent(r, c, "ERROR" + errorCode);
    }

    protected void parseEmptyStringFormula() {
        sheetStream.setContent(r, c, "");
    }

    protected void parseStringFormula() {
        ReadOnlyRecord nextRecord = new ReadOnlyRecord();
        RecordReader recordReader = context.getStreamReader();
        int count = 1;
        try {
            while (recordReader.nextRecord(nextRecord)
                    && nextRecord.getType() != Types.STRING && count < 4) {
                count++;
            }
        } catch (ReadException e) {
        }
        
        int len = nextRecord.getContentLength();
        byte[] stringData = new byte[len];
        System.arraycopy(nextRecord.getBytes(), off, stringData, 0, len);
        String value = buildString(stringData);
        sheetStream.setContent(r, c, value);
    }

    private String buildString(byte[] d) {
        String value;
        int pos = 0;
        int chars = NumUtil.getInt(d[0], d[1]);

        if (chars == 0) {
            value = "";
            return value;
        }
        pos += 2;
        int optionFlags = d[pos];
        pos++;

        if ((optionFlags & 0xf) != optionFlags) {
            // Uh oh - looks like a plain old string, not unicode
            // Recalculate all the positions
            pos = 0;
            chars = NumUtil.getInt(d[0], (byte) 0);
            optionFlags = d[1];
            pos = 2;
        }

        // See if it is an extended string
        boolean extendedString = ((optionFlags & 0x04) != 0);

        // See if string contains formatting information
        boolean richString = ((optionFlags & 0x08) != 0);

        if (richString) {
            pos += 2;
        }

        if (extendedString) {
            pos += 4;
        }

        // See if string is ASCII (compressed) or unicode
        boolean asciiEncoding = ((optionFlags & 0x01) == 0);

        if (asciiEncoding) {
            value = StringUtil.getASCIIString(d, pos, chars);
        } else {
            value = StringUtil.getUnicodeString(d, pos, chars);
        }
        return value;
    }
}
